package com.angle3d;

import android.graphics.Bitmap;
import android.opengl.GLES20;
import android.opengl.GLUtils;

/**
 * Class representing an OpenGL 2D texture.
 * @author Alessandro Boccalatte
 */
public class Texture {

	/**
	 * The maximum number of texture units allowed.
	 */
	public static final int MAX_TEXTURE_UNITS = 2;
	
	public enum WrapMode {
		REPEAT,
		CLAMP,
	};

	public enum Filter {
		NEAREST,
		LINEAR,
	};
	
	private WrapMode mWrapMode = WrapMode.REPEAT;
	private Filter mMinFilter = Filter.LINEAR;
	private Filter mMagFilter = Filter.LINEAR;
	
	private Bitmap mBitmap;	
	private int mTexId = -1;
	private boolean mInitialized = false;

	/**
	 * Specifies the bitmap from which this texture will be generated.
	 * @param bitmap a bitmap
	 */
	public void setBitmap(Bitmap bitmap) {
		if (mBitmap == bitmap) {
			return;
		}
		mBitmap = bitmap;
		mInitialized = false;
	}
	
	/**
	 * Invalidates the texture content, forcing a refresh of all pixels at the next rendering pass.
	 */
	public void invalidate() {
		mInitialized = false;
	}
	
	private void init() {
		if (mTexId > 0) {
			GLES20.glDeleteTextures(1, new int[]{mTexId}, 0);
			mTexId = -1;
		}
		
		//generate new texture
		int[] textures = new int[1];
		GLES20.glGenTextures(1, textures, 0);
		mTexId = textures[0];
		
		//bind texture
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexId);
		applyTexParams();
		
		//set texture data
		if (mBitmap != null)
			GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmap, 0);
		else
			GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, 32, 32, 0, 
								GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, null);
		
		mInitialized = true;
	}
	
	public void bind(ShaderProgram shader, String shaderTextureName, int unit) {
		if (!mInitialized) {
			init();
		}
		
		GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + unit);
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexId);
		applyTexParams();
		
		shader.setUniformValue(shaderTextureName, unit);
	}
	
	/**
	 * Sets the texture wrap mode.
	 * @param wrapMode the texture wrap mode.
	 */
	public void setWrapMode(WrapMode wrapMode) {
		mWrapMode = wrapMode;
	}
	
	/**
	 * Returns the texture wrap mode.
	 * @return the texture wrap mode.
	 */
	public WrapMode getWrapMode() {
		return mWrapMode;
	}
	
	/**
	 * Sets the texture minification filter.
	 * @param filter the texture minification filter.
	 */
	public void setMinificationFilter(Filter filter) {
		mMinFilter = filter;
	}
	
	/**
	 * Returns the texture minification filter.
	 * @return the texture minification filter.
	 */
	public Filter getMinificationFilter() {
		return mMinFilter;
	}
	
	/**
	 * Sets the texture magnification filter.
	 * @param filter the texture magnification filter.
	 */
	public void setMagnificationFilter(Filter filter) {
		mMagFilter = filter;
	}
	
	/**
	 * Returns the texture magnification filter.
	 * @return the texture magnification filter.
	 */
	public Filter getMagnificationFilter() {
		return mMagFilter;
	}
	
	private void applyTexParams() {
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, getGLWrapMode(mWrapMode));
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, getGLWrapMode(mWrapMode));
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, getGLFilter(mMinFilter));	
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, getGLFilter(mMagFilter));	
	}
	
	private static int getGLWrapMode(WrapMode wrapMode) {
		switch(wrapMode) {
		case CLAMP: return GLES20.GL_CLAMP_TO_EDGE; 
		case REPEAT: return GLES20.GL_REPEAT;
		}
		return GLES20.GL_CLAMP_TO_EDGE;
	}
	 
	private static int getGLFilter(Filter filter) {
		switch(filter) {
		 case LINEAR: return GLES20.GL_LINEAR;
		 case NEAREST: return GLES20.GL_NEAREST;
		
		}
		return GLES20.GL_NEAREST;
	}
	
	/**
	 * Unbinds the textures from all the texture units.
	 */
	public static void unbindAll() {
		for (int i = MAX_TEXTURE_UNITS; i >=0; i--) {
			GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
		}
	}
}

